/* 
 * File:   Action.hxx
 * Author: enric
 *
 * Created on 26 de enero de 2012, 19:07
 */

#ifndef ACTION_HXX
#define	ACTION_HXX

#include "State.hxx"

/**
 * Abstract Action Definition
 */
class Action
{
public:
        Action( unsigned id, std::string name );
        virtual ~Action(){}

        virtual bool           applicable( State* s ) const = 0;
        virtual State*         execute( State* s ) = 0;
        int                    cost() { return m_cost; }
	std::string            to_string() const { return m_name; }

        unsigned       identifier() const { return m_identifier; }
protected:
        unsigned    m_identifier;
	std::string m_name;
        int         m_cost;

};

/**
 * Move agent from src to dst
 */
class Move : public Action
{
public:
	Move( unsigned src_id, unsigned dst_id , unsigned id, std::string name );
	~Move();

        bool		applicable( State* s ) const;
        State*		execute( State* s );

protected:

	unsigned	m_source_id;
	unsigned	m_destination_id;
};

/**
 * the Agent location is the current_cell
 * and the destination is unlocked.
 */
inline bool	Move::applicable( State* s ) const
{
	return (s->agent()->location()->identifier() == m_source_id )
                && ( ! s->cells()[ m_destination_id ]->locked() );
}

inline State*   Move::execute( State* s )
{
	State* sp = new State( s );

	sp->agent()->set_location( sp->cells()[ m_destination_id ] );

	// move also key location if agent is holding a key
	if( ! sp->agent()->arm_empty() )
		sp->agent()->key_holding()->set_location( sp->cells()[ m_destination_id ] );

	return sp;
}

/**
 * Agent Pickup a key
 */

class PickUp : public Action
{
public:
	PickUp( unsigned cell_id, unsigned key_id, unsigned id, std::string name );
	~PickUp();

	bool          applicable( State* s ) const;
	State*        execute( State* s );
protected:
	unsigned      m_cell_id;
	unsigned      m_key_id;

};

/**
 * Agent location is key location and arm_empty
 */
inline bool	PickUp::applicable( State* s ) const
{
	return ( s->agent()->location()->identifier() == m_cell_id )
		&& ( s->keys()[ m_key_id ]->location()->identifier() == m_cell_id )
                && ( s->agent()->arm_empty() );
}

inline State*   PickUp::execute( State* s )
{
	State* sp = new State( s );

	sp->agent()->set_key_holding( sp->keys()[ m_key_id ] );

	return sp;
}

/**
 * Agent Puts down a key
 */

class PutDown : public Action
{
public:
	PutDown( unsigned cell_id, unsigned key_id, unsigned id, std::string name );
	~PutDown();

	bool          applicable( State* s ) const;
	State*        execute( State* s );
protected:
	unsigned      m_cell_id;
	unsigned      m_key_id;

};

/**
 * Agent location is key location and agent holding the key
 */
inline bool	PutDown::applicable( State* s ) const
{
	if( s->agent()->arm_empty() )
		return false;

	return ( s->agent()->location()->identifier() == m_cell_id )
		&& ( s->keys()[ m_key_id ]->location()->identifier() == m_cell_id )
		&& ( s->agent()->key_holding()->identifier() == m_key_id );

}

inline State*   PutDown::execute( State* s )
{
	State* sp = new State( s );

	sp->agent()->set_key_holding( NO_KEY );

	return sp;

}

/**
 * Agent opens a locked cell
 */
class Open : public Action
{
public:
	Open( unsigned src_id, unsigned dst_id, unsigned key_id, unsigned id, std::string name );
	~Open();

	bool          applicable( State* s ) const;
	State*        execute( State* s );
protected:
	unsigned      m_source_id;
        unsigned      m_destination_id;
	unsigned      m_key_id;

};

/**
 * Agent in src cell, holding the key, destination cell locked
 * and the key shape fits in the keyhole of the destination cell (adjacent)
 */
inline bool	Open::applicable( State* s ) const
{
	if( s->agent()->arm_empty() )
		return false;

	return (s->agent()->location()->identifier() == m_source_id )
                && ( s->agent()->key_holding()->identifier() == m_key_id )
		&& ( s->cells()[ m_destination_id ]->locked() )
                && ( s->keys()[ m_key_id ]->shape() == s->cells()[ m_destination_id ]->keyhole() );
}

inline State*   Open::execute( State* s )
{
	State* sp = new State( s );

	sp->cells()[ m_destination_id ]->locked() = false;

	return sp;
}

#endif	/* ACTION_HXX */
